package org.example.slave.packet;

import lombok.Data;
import lombok.EqualsAndHashCode;
import org.example.slave.constants.Capability;
import org.example.slave.util.ByteUtil;

import static org.example.slave.constants.MysqlConstant.PROTOCOL_VERSION;

/**
 * @author dingrui
 * @since 2022/2/15
 * @description
 */

/**
 *
 * <pre>
 *     +-------+ +------------------+ +------------------------+ +-----------+ +---------------+ +-----------+ +---------------------+ +-------------+ +---------------+ +------------+ +-----------------------+
 *     | bytes | |           1      | | n(Null-terminated str) | |     4     | |       8       | | 1(filler) | |          2          | |      1      | |       2       | | 13(filler) | |         13            |
 *     +-------+ +------------------+ +------------------------+ +-----------+ +---------------+ +-----------+ +---------------------+ +-------------+ +---------------+ +------------+ +-----------------------+
 *     | name  | | protocol_version | |     server_version     | | thread_id | | scramble_buff | |    0x0    | | server_capabilities | | server_lang | | server_status | |     0x0    | | rest of scramble_buff |
 *     +-------+ +------------------+ +------------------------+ +-----------+ +---------------+ +-----------+ +---------------------+ +-------------+ +---------------+ +------------+ +-----------------------+
 * </pre>
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class HandshakePacket extends PacketWithHeader {

    public byte protocolVersion = PROTOCOL_VERSION;
    public String serverVersion;
    public long threadId;
    public byte[] seed;
    public int serverCapabilities;
    public byte serverCharsetNumber;
    public int serverStatus;
    public byte[] restOfScrambleBuff;
    public byte[] authPluginName;

    public HandshakePacket() {
    }

    public HandshakePacket(byte[] data) {
        this.fromBytes(data);
    }

    @Override
    public void fromBytes(byte[] data) {
        int index = 0;
        // 1. read protocol_version
        this.protocolVersion = data[index];
        index++;
        // 2. read server_version
        byte[] serverVersionBytes = ByteUtil.readNullTerminatedBytes(data, index);
        this.serverVersion = new String(serverVersionBytes);
        index += (serverVersionBytes.length + 1);
        // 3. read thread_id
        this.threadId = ByteUtil.readUnsignedIntLittleEndian(data, index);
        index += 4;
        // 4. read scramble_buff
        this.seed = ByteUtil.readFixedLengthBytes(data, index, 8);
        index += 8;
        index += 1; // 1 byte (filler) always 0x00
        // 5. read server_capabilities
        this.serverCapabilities = ByteUtil.readUnsignedShortLittleEndian(data, index);
        index += 2;
        if (data.length > index) {
            // 6. read server_language
            this.serverCharsetNumber = data[index];
            index++;
            // 7. read server_status
            this.serverStatus = ByteUtil.readUnsignedShortLittleEndian(data, index);
            index += 2;
            // 8. bypass filtered bytes
            int capabilityFlags2 = ByteUtil.readUnsignedShortLittleEndian(data, index);
            index += 2;
            int capabilities = (capabilityFlags2 << 16) | this.serverCapabilities;
            // int authPluginDataLen = -1;
            // if ((capabilities & Capability.CLIENT_PLUGIN_AUTH) != 0) {
            // authPluginDataLen = data[index];
            // }
            index += 1;
            index += 10;
            // 9. read rest of scramble_buff
            if ((capabilities & Capability.CLIENT_SECURE_CONNECTION) != 0) {
                // int len = Math.max(13, authPluginDataLen - 8);
                // this.authPluginDataPart2 =
                // buffer.readFixedLengthString(len);// scramble2

                // Packet规定最后13个byte是剩下的scrumble,
                // 但实际上最后一个字节是0, 不应该包含在scrumble中.
                this.restOfScrambleBuff = ByteUtil.readFixedLengthBytes(data, index, 12);
            }

            index += 12 + 1;
            if ((capabilities & Capability.CLIENT_PLUGIN_AUTH) != 0) {
                this.authPluginName = ByteUtil.readNullTerminatedBytes(data, index);
            }
            // end read
        }
    }
}
